﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;
using DotNetNuke.Framework;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Users;
using DotNetNuke.Entities.Users.Social;
using DotNetNuke.Entities.Profile;
using DotNetNuke.Security.Roles;
using DotNetNuke.Security.Membership;
using DotNetNuke.Services.Localization;
using DotNetNuke.Services.Exceptions;
using DotNetNuke.Services.FileSystem;
using DotNetNuke.UI.WebControls;
using DotNetNuke.Security.Permissions;
using DotNetNuke.Instrumentation;

namespace WESNet.DNN.Modules.UserExport
{
    public class Exporter
    {
        private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(Exporter));
        
        #region Private Members

        PortalSettings _activePortalSettings;
        int _activePortalId;

        PortalSettings _selectedPortalSettings;
        int _selectedPortalId;

        UserInfo _sendingUser;
        bool _isAdministrator;
        bool _isSuperUser;
        
        Mailer _mailer;

        HttpResponse _response;
        string _host = "";

        Dictionary<string, UserInfo> _usersToExport;
        Dictionary<int, string> _ListEntryValues = new Dictionary<int, string>();
        
        WriteHeaderFooter _writeHeader = null;
        WriteUser _writeUser = null;
        WriteHeaderFooter _writeFooter = null;

        IList<RelationshipType> _relationshipTypes;
        IList<Relationship> _relationships;

        #endregion

        #region Property Backing Stores 
     
        ExportFormats _exportFormat = ExportFormats.CSV;     
        string _exportFolderpath = null;
        int _exportFolderID = -1;
        string _exportFilename = null;
        long _ExportedCount = 0;
        long _SkippedCount = 0;

        ExportStatuses _ExportStatus;
        UserInfo _LastProcessedUser;
        List<string> _ExporterMessages = new List<string>();
        
        #endregion


        #region Public Properties

        public int ActivePortalId
        {
            get
            {
                return _activePortalId;
            }
        }

        public PortalSettings ActivePortalSettings
        {
            get
            {
                return _activePortalSettings;
            }
        }

        public int SelectedPortalId
        {
            get
            {
                return _selectedPortalId;
            }
            set
            {
                _selectedPortalId = value;
                _selectedPortalSettings = value != _activePortalId ? new PortalSettings(value): _selectedPortalSettings;
            }
        }

        public PortalSettings SelectedPortalSettings
        {
            get
            {
                return _selectedPortalSettings;
            }
        }
        
        public List<string> ExportRoles { get; set; }
        public List<string> ExcludeRoles { get; set; }
        
        public Dictionary<int, ProfileProperty> SelectedProfileProperties { get; set; }

        public bool IncludeUnapproved { get; set; }
        public bool IncludeSoftDeleted { get; set; }

        public DateRange CreatedOnDateRange { get; set; }
        public DateRange LastLoginDateRange { get; set; }

        public ExportFormats ExportFormat
        {
            get
            {
                return _exportFormat;
            }
            set
            {
                _exportFormat = value;
                if (_exportFilename != null)
                {
                    _exportFilename = Path.ChangeExtension(_exportFilename, Consts.FileExtensions[(int)value]);
                }
            }
        }

        public ExportDestinations ExportDestination { get; set; }

        public EmailOptions EmailOption { get; set; }

        public bool ProcessAsynch { get; set; }

        public bool OpenDownloadInNewWindow { get; set; }

        public bool ExportCSVHeaderRow { get; set; }
        public char CSVDelimiter { get; set; }
        public bool CSVQuoteValues { get; set; }

        public bool IncludeUserRoleInformation { get; set; }
        public bool IncludeRelationshipInformation { get; set; }
        
        public bool ExportXLSHeaderRow { get; set; }

        public string ExportFolderpath
        {
            get
            {
                return _exportFolderpath;
            }
            set
            {
                var folderInfo = FolderManager.Instance.GetFolder(_activePortalId, value);
                if (folderInfo != null)
                {
                    _exportFolderpath = folderInfo.FolderPath;
                    _exportFolderID = folderInfo.FolderID;
                }
                else
                {
                    throw new FolderProviderException("Folder '" + value + "' does not exist.");
                }
            }
        }

        public int ExportFolderID
        {
            get
            {
                return _exportFolderID;
            }
            set
            {
                var folderInfo = FolderManager.Instance.GetFolder(value);
                if (folderInfo != null)
                {
                    _exportFolderpath = folderInfo.FolderPath;
                    _exportFolderID = folderInfo.FolderID;
                }
                else
                {
                    throw new FolderProviderException("FolderID '" + value.ToString() + "' does not exist.");
                }
            }
        }

        public string ExportFilename
        {
            get
            {
                return _exportFilename;
            }
            set
            {
                _exportFilename = Utilities.CleanseFilename(value, ExportFormat);
            }
        }

        public string ExportFilepath
        {
            get
            {
                var folderInfo = FolderManager.Instance.GetFolder(ExportFolderID);
                return folderInfo == null ? "" : folderInfo.PhysicalPath + ExportFilename;
            }
        }

        public string EmailTo
        {
            get
            {
                return _mailer.EmailTo;
            }
            set
            {
                _mailer.EmailTo = value;
            }
        }

        public List<string> EmailToRoles
        {
            get
            {
                return _mailer.EmailToRoles;
            }
            set
            {
                _mailer.EmailToRoles = value;
            }
        }

        public string EmailFrom
        {
            get
            {
                return _mailer.EmailFrom;
            }
            set
            {
                _mailer.EmailFrom = value;
            }
        }

        public string LocalResourceFile { get; set; }

        public ExportStatuses ExportStatus
        {
            get
            {
                return _ExportStatus;
            }
        }

        public UserInfo LastProcessedUser
        {
            get
            {
                return _LastProcessedUser;
            }
        }

        public List<string> ExporterMessages
        {
            get
            {
                return _ExporterMessages;
            }
        }

        public long ExportedCount
        {
            get
            {
                return _ExportedCount;
            }
        }

        public long SkippedCount
        {
            get
            {
                return _SkippedCount;
            }
        }

        public long ProcessedCount
        {
            get
            {
                return _ExportedCount + _SkippedCount;
            }
        }

        #endregion

        #region Constructors

        public Exporter(int moduleId)
        {
            _activePortalSettings = PortalController.Instance.GetCurrentPortalSettings();
            _activePortalId = _activePortalSettings.PortalId;

            _selectedPortalId = _activePortalId;
            _selectedPortalSettings = _activePortalSettings;

            _sendingUser = UserController.Instance.GetCurrentUserInfo();
            _isSuperUser = _sendingUser.IsSuperUser;
            _isAdministrator = (_sendingUser.IsInRole("Administrators") || _isSuperUser);

            //_rootDirectoryPath = _isSuperUser ? Globals.HostPath : _portalSettings.HomeDirectory;
            
            ExportDestination = ExportDestinations.PortalFile;
            EmailOption = EmailOptions.None;
            CreatedOnDateRange = DateRange.Empty;
            LastLoginDateRange = DateRange.Empty;
            OpenDownloadInNewWindow = true;
            ExportCSVHeaderRow = true;
            CSVDelimiter = ',';
            CSVQuoteValues = true;
            _mailer = new Mailer(moduleId);
            _mailer.SendingUser = _sendingUser;

            if (HttpContext.Current != null)
            {
                _response = HttpContext.Current.Response;
                _host = HttpContext.Current.Request.Url.Host;
            }
        }

        public Exporter(int moduleId, int selectedPortalId, ExportFormats exportFormat, ExportDestinations exportDestination) : this(moduleId)
        {
            SelectedPortalId = selectedPortalId;
            ExportFormat = exportFormat;
            ExportDestination = exportDestination;
            _mailer.PortalId = selectedPortalId;

        }
        
        #endregion

        #region Delegates

        private delegate void WriteUser(XmlTextWriter writer, UserInfo user);
        private delegate void WriteHeaderFooter(XmlTextWriter writer);

        #endregion

        #region Public Methods

        public void ExportUsers()
        {
            if (ExportDestination == ExportDestinations.PortalFile && EmailOption != EmailOptions.None)
            {
                var recipientCount = _mailer.BuildRecipientsList();
                if (recipientCount == 0)
                {
                    AddExporterMessage(ExportStatuses.NoEmailRecipients, "No email recipients specified", null, null, false);
                    return;
                }
                var subject = EmailOption == EmailOptions.Link ? Localization.GetString("EmailSubjectWithLink", LocalResourceFile) : Localization.GetString("EmailSubjectWithAttachment", LocalResourceFile);
                var body = EmailOption == EmailOptions.Link ? Localization.GetString("EmailBodyWithLink", LocalResourceFile) : Localization.GetString("EmailBodyWithAttachment", LocalResourceFile);
                if (subject == null || body == null)
                {
                    AddExporterMessage(ExportStatuses.NoEmailSubjectOrBody, "Unable to obtain email subject and/or body text from LocalResourceFile '" + LocalResourceFile + "'.", null, null, true);
                    return;
                }
                _mailer.Subject = subject;
                _mailer.Body = body;
            }

            _ExportedCount = 0;
            _SkippedCount = 0;
            _ExporterMessages.Clear();
            
            if (ProcessAsynch)
            {
                _ExportStatus = ExportStatuses.AsynchExportStarted;
                var thread = new System.Threading.Thread(() => PerformExport());
                thread.Start();
            }
            else
            {
                _ExportStatus = ExportStatuses.ExportSuccess;
                PerformExport();
            }
        }

        public void PerformExport()
        {
            XmlTextWriter writer = null;
            MemoryStream stream = null;

            switch (ExportFormat)
            {
                case ExportFormats.CSV:
                    _writeHeader = WriteCSVHeader;
                    _writeUser = WriteCSVUser;
                    _writeFooter = WriteCSVFooter;
                    break;
                case ExportFormats.XML:
                    _writeHeader = WriteXMLHeader;
                    _writeUser = WriteXMLUser;
                    _writeFooter = WriteXMLFooter;
                    break;
                case ExportFormats.XLS:
                    _writeHeader = WriteXLSHeader;
                    _writeUser = WriteXLSUser;
                    _writeFooter = WriteXLSFooter;
                    break;
            }

            _usersToExport = GetUsersToExport();

            try
            {
                switch (ExportDestination)
                {
                    case ExportDestinations.PortalFile:
                        stream = new MemoryStream();
                        writer = new XmlTextWriter(stream, Encoding.UTF8);
                        break;
                    case ExportDestinations.Download:
                        _response.ClearContent();
                        _response.ClearHeaders();
                        _response.ContentType = Consts.MimeTypes[(int)ExportFormat] + "; charset=utf-8";
                        _response.AppendHeader("content-disposition", (OpenDownloadInNewWindow ? "attachment" : "inline") + ";filename=" + ExportFilename);
                        //Response.AddHeader("content-length", s.BaseStream.Length.ToString());
                        writer = new XmlTextWriter(_response.OutputStream, Encoding.UTF8);
                        break;
                }

                _writeHeader(writer);

                foreach (var kvp in _usersToExport)
                {
                    _LastProcessedUser = kvp.Value;
                    _writeUser(writer, kvp.Value);
                    _ExportedCount++;
                }
                _writeFooter(writer);
            }
            catch (Exception exc)
            {
                AddExporterMessage(ExportStatuses.GeneralExportException, null, _LastProcessedUser, exc, true);
                return;
            }
            finally
            {
                if (writer != null)
                {
                    writer.Flush();
                }

                if (!(_ExportStatus == ExportStatuses.ExportSuccess || _ExportStatus == ExportStatuses.AsynchExportStarted))
                {
                    writer.Close();
                    writer = null;
                }
            }
                        
            switch (ExportDestination)
            {
                    case ExportDestinations.PortalFile:
                        try
                        {
                            stream.Seek(0, SeekOrigin.Begin);
                            var folderInfo = FolderManager.Instance.GetFolder(_exportFolderID);
                            var fileInfo = FileManager.Instance.AddFile(folderInfo, _exportFilename, stream, true);
                            SendNotifications(fileInfo, _ExportedCount, _SkippedCount);
                        }
                        catch (Exception exc)
                        {
                            AddExporterMessage(ExportStatuses.FileSystemError, "Unable to save export filestream to DotNetNuke file system", null, exc, true);
                            return;
                        }
                        finally
                        {
                            if (stream != null)
                            {
                                stream.Close();
                            }
                        } 
                        break;
            
                    case ExportDestinations.Download:
                        _response.Flush();
                        //_response.End();
                        _response.ClearContent();
                        _response.Close();
                        break;
            }
        }

        #endregion

        #region Private Methods

        private void AddExporterMessage(ExportStatuses status, string message, UserInfo user, Exception exc, bool logit)
        {
            _ExportStatus = status;
            if (user != null) _LastProcessedUser = user;
            if (exc != null && _isAdministrator)
            {
                message += (string.IsNullOrEmpty(message) ? "" : "<br />") + Localization.GetString("ExceptionMessage", LocalResourceFile) + ": " + exc.Message;
                if (_isSuperUser)
                {
                    message += "<br />" + Localization.GetString("ExceptionDetails", LocalResourceFile) + ": " + exc.ToString();
                }
            }

            if (message.Length > 0) _ExporterMessages.Add(message);

            if (logit)
            {

                Logger.Error(message, exc);
            }
        } 

        private void SendNotifications(IFileInfo fileInfo, long exportedCount, long skippedCount)
        {
            if (ExportDestination == ExportDestinations.PortalFile && EmailOption != EmailOptions.None)
            {
                try
                {
                    if (EmailOption == EmailOptions.Attachment) _mailer.AddAttachment(fileInfo.PhysicalPath);

                    var customObject = new { ExportFileLink = GetExportFileLink(fileInfo), ExportFilepath = fileInfo.PhysicalPath, ExportedCount = exportedCount, SkippedCount = skippedCount };

                    _mailer.AddTokenizationDataSource("ExportUsers", customObject);

                    var result = _mailer.Send();
                    if (result.Length > 0)
                    {
                        AddExporterMessage(ExportStatuses.GeneralMailerException, result, null, null, true);
                    }

                }
                catch (Exception exc)
                {
                    AddExporterMessage(ExportStatuses.GeneralMailerException, "", null, exc, true);
                }
            }
        }

        private string GetExportFileLink(IFileInfo fileInfo)
        {
            if (fileInfo == null)
            {
                return string.Empty;
            }
            else
            {                 
                var linkClickUrl = Globals.LinkClick("FileID=" + fileInfo.FileId.ToString(), -1, -1, false, true, SelectedPortalId, SelectedPortalSettings.EnableUrlLanguage, SelectedPortalSettings.GUID.ToString());
                if (linkClickUrl.StartsWith("/"))
                {
                    linkClickUrl = Globals.AddHTTP(_host) + linkClickUrl;
                }
                return linkClickUrl;
            }
        }

        private string GetPropertyValue(UserInfo user, KeyValuePair<int, ProfileProperty> profileProperty)
        {
            string value = null;
            ProfilePropertyDefinition ppd;

            switch (profileProperty.Key)
            {
                case -7: //Username
                    value = user.Username;
                    break;
                case -6: //DisplayName
                    value = user.DisplayName;
                    break;
                case -5: //Email
                    value = user.Email;
                    break;
                case -4: //Password
                    value = GetDecryptedPassword(user);
                    break;
                case -3: //LastActivityDate
                    value = user.Membership.LastActivityDate.ToString("o");
                    break;
                case -2: //LastLoginDate
                    value = user.Membership.LastLoginDate.ToString("o");
                    break;
                case -1: //CreatedOnDate
                    value = user.Membership.CreatedDate.ToString("o");
                    break;
                default:
                    ppd = user.Profile.ProfileProperties.GetById(profileProperty.Key);
                    if (ppd == null)
                    {
                        value = string.Empty;
                    }
                    else
                    {
                        value = ppd.PropertyValue;
                        int listEntryID;
                        if (string.IsNullOrEmpty(value))
                        {
                            value = ppd.DefaultValue;
                        }
                        else if ((ppd.PropertyName == "Country" || ppd.PropertyName == "Region") && int.TryParse(value, out listEntryID))
                        {
                            string listEntryText = value;
                            if (!_ListEntryValues.TryGetValue(listEntryID, out listEntryText))
                            {
                                var listEntryInfo = new DotNetNuke.Common.Lists.ListController().GetListEntryInfo(listEntryID);
                                if (listEntryInfo != null)
                                {
                                    _ListEntryValues.Add(listEntryID, listEntryInfo.Text);
                                    listEntryText = listEntryInfo.Text;
                                }
                            }
                            value = listEntryText;
                        }
                    }
                    break;
            }

            return (value == null ? string.Empty : value);
        }

        private string GetDecryptedPassword(UserInfo user)
        {
            if (MembershipProvider.Instance().PasswordFormat == PasswordFormat.Hashed)
            {
                return string.Empty;
            }

            if (!_isAdministrator)
            {
                return "********";
            }

            return UserController.GetPassword(ref user, user.Membership.PasswordAnswer);
        }

        private bool IsUserRoleActive(UserRoleInfo userRoleInfo, DateTime now)
        {
            return (userRoleInfo.EffectiveDate <= now || Null.IsNull(userRoleInfo.EffectiveDate))
                            && (userRoleInfo.ExpiryDate >= now || Null.IsNull(userRoleInfo.ExpiryDate));
        }

        private Dictionary<string, UserInfo> GetUsersToExport()
        {
            var usersToExport = new Dictionary<string, UserInfo>();

            var uc = new UserController();
            var rc = new RoleController();

            RoleInfo roleInfo;
            var now = DateTime.Now;

            foreach (string roleName in ExportRoles)
            {
                roleInfo = rc.GetRoleByName(SelectedPortalId, roleName);

                foreach (UserInfo user in RoleController.Instance.GetUsersByRole(SelectedPortalId, roleName))
                {
                    if ((!(user.Membership.Approved || IncludeUnapproved)) ||
                        (user.IsDeleted && !IncludeSoftDeleted) ||
                        !CreatedOnDateRange.InRange(user.CreatedOnDate)||
                        !LastLoginDateRange.InRange(user.Membership.LastLoginDate) ||
                        (user.Roles.Intersect(ExcludeRoles).Count() > 0))
                    {
                        _SkippedCount ++;
                        continue;
                    }
                    
                    UserRoleInfo userRoleInfo = rc.GetUserRole(SelectedPortalId, user.UserID, roleInfo.RoleID);
                    if (!IsUserRoleActive(userRoleInfo, now))
                    {
                        _SkippedCount++;
                    }
                    else if (!usersToExport.ContainsKey(user.Username))
                    {
                        usersToExport.Add(user.Username, user);
                    }
                }
            }
            return usersToExport;
        }

        #endregion

        #region CSV Writers

        private void WriteCSVHeader(XmlTextWriter writer)
        {
            if (ExportCSVHeaderRow)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var kvp in SelectedProfileProperties)
                {
                    if (kvp.Value.Selected)
                    {
                        AppendValueDelimited(sb, kvp.Value.PropertyName);
                    }
                }
                if (sb.Length > 0) sb.Length -= 1; //remove trailing slash
                sb.AppendLine();
                writer.WriteString(sb.ToString());
            }
        }

        private void WriteCSVUser(XmlTextWriter writer, UserInfo user)
        {
            var sb = new StringBuilder();

            ProfileController.GetUserProfile(ref user); //user profile does not auto-hydrate

            foreach (var kvp in SelectedProfileProperties)
            {
                if (kvp.Value.Selected)
                {
                    var value = GetPropertyValue(user, kvp);
                    AppendValueDelimited(sb, value);
                }
            }
            if (sb.Length > 0) sb.Length -= 1; //remove trailing delimeter
            sb.AppendLine();
            writer.WriteString(sb.ToString());
        }

        private void WriteCSVFooter(XmlTextWriter writer)
        {
            // There is no CSV footer so nothing to write
        }

        private void AppendValueDelimited(StringBuilder sb, string value)
        {
            string singleQuote = "\"";
            string doubleQuotes = "\"\"";

            if (CSVQuoteValues || value.Contains(CSVDelimiter))
            {
                value = singleQuote + value.Replace(singleQuote, doubleQuotes) + singleQuote;
            }
            sb.Append(value);
            sb.Append(CSVDelimiter);
        }

        #endregion

        #region XML Writers

        private void WriteXMLHeader(XmlTextWriter writer)
        {
            writer.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
            writer.WriteStartElement("userExport");
            writer.WriteAttributeString("exportedOnDate", DateTime.Now.ToString("o"));
            writer.WriteStartElement("portals");
            writer.WriteStartElement("portal");
            writer.WriteAttributeString("portalName", new PortalController().GetPortal(SelectedPortalId).PortalName);
            writer.WriteAttributeString("portalId", SelectedPortalId.ToString());
            if (IncludeUserRoleInformation)
            {
                var roles = RoleController.Instance.GetRoles(SelectedPortalId);
                writer.WriteStartElement("roles");
                foreach (RoleInfo role in roles)
                {
                    role.WriteXml(writer);
                }
                writer.WriteEndElement(); //</roles>
            }


            if (IncludeRelationshipInformation)
            {
                _relationshipTypes = RelationshipController.Instance.GetAllRelationshipTypes();
                writer.WriteStartElement("relationshipTypes");
                foreach (RelationshipType relationshipType in _relationshipTypes)
                {
                    WriteXMLRelationshipType(writer, relationshipType);
                }
                writer.WriteEndElement(); //</relationshipTypes>

                _relationships = RelationshipController.Instance.GetRelationshipsByPortalId(SelectedPortalId);
                writer.WriteStartElement("relationships");
                foreach (Relationship relationship in _relationships)
                {
                    WriteXMLRelationship(writer, relationship);
                }
                writer.WriteEndElement(); //</relationships>
            }  
                
            writer.WriteStartElement("users");
        }

        private void WriteXMLRelationshipType(XmlTextWriter writer, RelationshipType relationshipType)
        {
            writer.WriteStartElement("relationshipType");
            writer.WriteAttributeString("direction", relationshipType.Direction.ToString("d"));
            writer.WriteAttributeString("description", relationshipType.Description);
            writer.WriteValue(relationshipType.Name);
            writer.WriteEndElement(); //</relationshipType>
        }

        private void WriteXMLRelationship(XmlTextWriter writer, Relationship relationship)
        {
            writer.WriteStartElement("relationship");
            writer.WriteAttributeString("relationshipType", _relationshipTypes.First(rt => rt.RelationshipTypeId == relationship.RelationshipTypeId).Name);
            writer.WriteAttributeString("description", relationship.Description);
            writer.WriteAttributeString("defaultResponse", relationship.DefaultResponse.ToString("d"));
            writer.WriteValue(relationship.Name);
            writer.WriteEndElement(); //</relationship>
        }

        private void WriteXMLUser(XmlTextWriter writer, UserInfo user)
        {
            writer.WriteStartElement("user");

            ProfileController.GetUserProfile(ref user); //user profile does not auto-hydrate

            foreach (var kvp in SelectedProfileProperties)
            {
                if (kvp.Value.Selected)
                {
                    var value = GetPropertyValue(user, kvp);
                    writer.WriteElementString(kvp.Value.PropertyName, value);
                }
            }

            if (IncludeUserRoleInformation)
            {
                writer.WriteStartElement("roles");
                var userRoles = RoleController.Instance.GetUserRoles(SelectedPortalId, user.Username, "");

                foreach (UserRoleInfo uri in userRoles)
                {
                    writer.WriteStartElement("role");
                    if (uri.ExpiryDate != Null.NullDate) writer.WriteAttributeString("expiryDate", uri.ExpiryDate.ToString("o"));
                    if (uri.EffectiveDate != Null.NullDate) writer.WriteAttributeString("effectiveDate", uri.EffectiveDate.ToString("o"));
                    if (uri.IsTrialUsed) writer.WriteAttributeString("isTrialUsed", uri.Subscribed.ToString());
                    if (uri.Subscribed) writer.WriteAttributeString("subscribed", uri.Subscribed.ToString());
                    writer.WriteValue(uri.RoleName);
                    writer.WriteEndElement(); //</role>
                }

                writer.WriteEndElement(); //</roles>
            }

            if (IncludeRelationshipInformation)
            {
                writer.WriteStartElement("ownedRelationships");
                var relationships = RelationshipController.Instance.GetRelationshipsByUserId(user.UserID);
                foreach (Relationship relationship in relationships)
                {
                    WriteXMLRelationship(writer, relationship);
                    _relationships.Add(relationship);
                }
                writer.WriteEndElement(); //</relationships>

                var userRelationships = user.Social.UserRelationships.Where(ur => ur.UserId == user.UserID);
                writer.WriteStartElement("userRelationships");
                foreach (UserRelationship userRelationship in userRelationships)
                {
                    WriteXMLUserRelationship(writer, userRelationship, user.PortalID);
                }
                writer.WriteEndElement(); //</userRelationships>
           }

            writer.WriteEndElement(); //</user>
        }

        private void WriteXMLUserRelationship(XmlTextWriter writer, UserRelationship userRelationship, int portalId)
        {
            writer.WriteStartElement("userRelationship");
            writer.WriteAttributeString("relationshipName", _relationships.First(r => r.RelationshipId == userRelationship.RelationshipId).Name);
            writer.WriteAttributeString("status", userRelationship.Status.ToString());
            writer.WriteAttributeString("relatedUsername", new UserController().GetUser(portalId, userRelationship.RelatedUserId).Username);
            writer.WriteEndElement(); //</userRelationship>
        }

        private void WriteXMLFooter(XmlTextWriter writer)
        {
            writer.WriteEndElement();               //</Users>
            writer.WriteEndElement();           //<Portal>
            writer.WriteEndElement();       //</Portals>
            writer.WriteEndElement();   //</UserExport>
        }

        #endregion

        #region XLS Writers

        private void WriteXLSHeader(XmlTextWriter writer)
        {
            writer.WriteProcessingInstruction("xml", " version=\"1.0\" encoding=\"utf-8\"");
            writer.WriteProcessingInstruction("mso-application", "progid=\"Excel.Sheet\"");

            //<Workbook>

            writer.WriteStartElement("Workbook", "urn:schemas-microsoft-com:office:spreadsheet");
            writer.WriteAttributeString("xmlns:html", "http://www.w3.org/TR/REC-html40");
            writer.WriteAttributeString("xmlns:o", "urn:schemas-microsoft-com:office:office");
            writer.WriteAttributeString("xmlns:x", "urn:schemas-microsoft-com:office:excel");
            writer.WriteAttributeString("xmlns:ms", "urn:schemas-microsoft-com:xslt");
            writer.WriteAttributeString("xmlns:ss", "urn:schemas-microsoft-com:office:spreadsheet");

            //<Worksheet>

            writer.WriteStartElement("Worksheet", "urn:schemas-microsoft-com:office:spreadsheet");
            string workSheetName = string.Format(Regex.Replace(Localization.GetString("ExcelWorksheetName", LocalResourceFile), @"[\[\]]", ""), SelectedPortalId);
            writer.WriteAttributeString("ss", "Name", "urn:schemas-microsoft-com:office:spreadsheet", workSheetName);

            //<Table>

            writer.WriteStartElement("Table");

            //Row (Column Titles)

            if (ExportXLSHeaderRow)
            {
                writer.WriteStartElement("Row");

                foreach (var pp in SelectedProfileProperties)
                {
                    if (pp.Value.Selected)
                    {
                        WriteXLSCell(writer, pp.Value.PropertyName, "String");
                    }
                }

                writer.WriteEndElement(); //</Row>
            }
        }

        private void WriteXLSUser(XmlTextWriter writer, UserInfo user)
        {
            writer.WriteStartElement("Row");

            foreach (var kvp in SelectedProfileProperties)
            {
                if (kvp.Value.Selected)
                {
                    var value = GetPropertyValue(user, kvp);
                    WriteXLSCell(writer, value, "String");
                }
            }
            writer.WriteEndElement(); //</Row>
        }

        private void WriteXLSCell(XmlTextWriter writer, string value, string dataType)
        {
            writer.WriteStartElement("Cell");
            writer.WriteStartElement("Data");
            writer.WriteAttributeString("ss", "Type", "urn:schemas-microsoft-com:office:spreadsheet", dataType);
            writer.WriteValue(value);
            writer.WriteEndElement(); //</Data>
            writer.WriteEndElement(); //</Cell>
        }

        private void WriteXLSFooter(XmlTextWriter writer)
        {
            writer.WriteEndElement(); //</Table>
            writer.WriteEndElement(); //</Worksheet>
            writer.WriteEndElement(); //</Workbook>
        }

        #endregion

    }
}